package demo.db.mybatis.plugin;

import java.lang.reflect.Method;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

/**
 * 拦截器的实现都是基于代理的设计模式实现的，简单的说就是要创造一个目标类的代理类，在代理类中执行目标类的方法并在方法之前执行拦截器代码。
 * <p>
 * MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下，MyBatis 允许使用插件来拦截的方法调用包括：
 * <li>Executor (update, query, flushStatements, commit, rollback,
 * getTransaction, close, isClosed)
 * <li>ParameterHandler (getParameterObject, setParameters)
 * <li>ResultSetHandler (handleResultSets, handleOutputParameters)
 * <li>StatementHandler (prepare, parameterize, batch, update, query)
 * <p>
 * 我们看到了可以拦截Executor接口的部分方法，比如update，query，commit，rollback等方法，还有其他接口的一些方法等。
 * 总体概括为：
 * <li>拦截执行器的方法
 * <li>拦截参数的处理
 * <li>拦截结果集的处理
 * <li>拦截Sql语法构建的处理
 * <p>
 * 全局xml配置：
 * 
 * <pre>
 * 
<plugins>
    <plugin interceptor="demo.db.mabatis.plugin.InterceptorDemo"></plugin>
</plugins>
 * 
 * </pre>
 * 
 * <li>Executor是 Mybatis的内部执行器，它负责调用StatementHandler操作数据库，并把结果集通过
 * ResultSetHandler进行自动映射，另外，他还处理了二级缓存的操作。从这里可以看出，我们也是可以通过插件来实现自定义的二级缓存的。
 * <li>StatementHandler是Mybatis直接和数据库执行sql脚本的对象。另外它也实现了Mybatis的一级缓存。这里，我们可以使用插件来实现对一级缓存的操作(禁用等等)。
 * <li>ParameterHandler是Mybatis实现Sql入参设置的对象。插件可以改变我们Sql的参数默认设置。
 * <li>ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口对象。我们可以定义插件对Mybatis的结果集自动映射进行修改。
 *
 * 
 * 这个拦截器拦截Executor接口的update方法（其实也就是SqlSession的新增，删除，修改操作），所有执行executor的update方法都会被该拦截器拦截到。
 */
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class InterceptorDemo implements Interceptor {

    /**
     * intercept方法就是要进行拦截的时候要执行的方法
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 被代理对象
        Object target = invocation.getTarget();
        // 代理方法
        Method method = invocation.getMethod();
        // 方法参数
        Object[] args = invocation.getArgs();
        // do something ...... 方法拦截前执行代码块
        Object result = invocation.proceed();
        System.out.println("Invocation.proceed()");
        return result;
    }

    /**
     * plugin方法是插件用于封装目标对象的，通过该方法我们可以返回目标对象本身，也可以返回一个它的代理，
     * 可以决定是否要进行拦截进而决定要返回一个什么样的目标对象，官方提供了示例：return Plugin.wrap(target, this);
     * 
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * setProperties方法是在Mybatis进行配置插件的时候可以配置自定义相关属性，即：接口实现对象的参数配置
     */
    @Override
    public void setProperties(Properties properties) {
        String prop1 = properties.getProperty("prop1");
        String prop2 = properties.getProperty("prop2");
        System.out.println(prop1 + "------" + prop2);
    }

}
